#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
from selenium.common.exceptions import NoSuchFrameException
from selenium.common.exceptions import NoAlertPresentException
from selenium.common.exceptions import WebDriverException
from selenium.common.exceptions import NoSuchCookieException
from selenium.common.exceptions import UnableToSetCookieException
from Config.GlobalVariables import RESULTS_SCREENSHOTS_PATH
from Config.GlobalVariables import WAIT_UNTIL_TIMEOUT, WAIT_FREQUENCY
from Utils.Logger import Logger
import time
import os


class PageUtil(object):

    def __init__(self, _driver):
        self.driver = _driver
        self.logger = Logger().get_logger()

    def get_element(self, locator, timeout=WAIT_UNTIL_TIMEOUT, frequency=WAIT_FREQUENCY):
        """
        通过定位器获取页面元素，每一段时间尝试获取一次，获取成功后返回获取的页面元素，获取失败后抛出 NoSuchElement 异常
        locator: 定位器，包含定位方式和定位表达式的元组，如("id", "username")
        timeout: 超时时间，默认为30秒
        frequency: 获取频率，默认为0.5秒
        """
        try:
            # 之所以使用lambda表达式是因为util接收的参数是method而不是element，所以不能显式调用find_element方法，
            # 而是将find_element方法作为参数传给until。同时method的参数是driver，所以这里lambda表达式接收的参数也是driver。
            # until 也可以使用expected_conditions，EC所有的类都是期望场景，实例方法__call__里接收参数driver。
            self.logger.debug("Get element by locator")
            element = WebDriverWait(self.driver, timeout, frequency).until(
                lambda _driver: _driver.find_element(by=locator[0], value=locator[1]), str(locator))
            return element
        except (NoSuchElementException, TimeoutException) as e:
            self.logger.error("NoSuchElementException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def get_elements(self, locator, timeout=WAIT_UNTIL_TIMEOUT, frequency=WAIT_FREQUENCY):
        """
        通过定位器获取页面一组元素，每一段时间尝试获取一次，获取成功后返回获取的页面元素列表，获取失败后抛出 NoSuchElement 异常
        locator: 定位器，包含定位方式和定位表达式的元组，如("id", "username")
        timeout: 超时时间，默认为30秒
        frequency: 获取频率，默认为0.5秒
        """
        try:
            self.logger.debug("Get elements by locator")
            elements = WebDriverWait(self.driver, timeout, frequency).until(
                lambda _driver: _driver.find_elements(by=locator[0], value=locator[1]), str(locator))
            return elements
        except (NoSuchElementException, TimeoutException) as e:
            self.logger.error("NoSuchElementException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def check_element(self, locator, timeout=WAIT_UNTIL_TIMEOUT, frequency=WAIT_FREQUENCY):
        try:
            self.logger.debug("Check element by locator")
            element = WebDriverWait(self.driver, timeout, frequency).until(
                lambda _driver: _driver.find_element(by=locator[0], value=locator[1]), str(locator))
            return element
        except TimeoutException:
            self.logger.warning("Cannot find element: {0}".format(locator))
            return False
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def check_elements(self, locator, timeout=WAIT_UNTIL_TIMEOUT, frequency=WAIT_FREQUENCY):
        try:
            self.logger.debug("Check elements by locator")
            elements = WebDriverWait(self.driver, timeout, frequency).until(
                lambda _driver: _driver.find_element(by=locator[0], value=locator[1]), str(locator))
            return elements
        except TimeoutException:
            self.logger.warning("Cannot find elements: {0}".format(locator))
            return False
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def switch_to_parent_frame(self):
        try:
            self.logger.debug("Switch to the parent frame")
            self.driver.switch_to.parent_frame()
            time.sleep(2)  # Chrome issue, have to wait sometime after switching frame
        except NoSuchFrameException as e:
            self.logger.error("NoSuchFrameException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def switch_to_default_frame(self):
        try:
            self.logger.debug("Switch to the default frame")
            self.driver.switch_to.default_content()
            time.sleep(2)  # Chrome issue, have to wait sometime after switching frame
        except NoSuchFrameException as e:
            self.logger.error("NoSuchFrameException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def get_alert_text(self):
        try:
            self.logger.debug("Get the alert text")
            text = self.driver.switch_to.alert.text
            return text
        except NoAlertPresentException as e:
            self.logger.error("NoAlertPresentException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def accept_alert(self):
        try:
            self.logger.debug("Accept the alert")
            self.driver.switch_to.alert.accept()
        except NoAlertPresentException as e:
            self.logger.error("NoAlertPresentException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def dismiss_alert(self):
        try:
            self.logger.debug("Dismiss the alert")
            self.driver.switch_to.alert.dismiss()
        except NoAlertPresentException as e:
            self.logger.error("NoAlertPresentException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def send_alert_keys(self, value):
        try:
            self.logger.debug("Send keys to the alert")
            self.driver.switch_to.alert.send_keys(value)
        except NoAlertPresentException as e:
            self.logger.error("NoAlertPresentException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def get_cookies(self):
        try:
            self.logger.debug("Get page cookies")
            self.driver.get_cookies()
        except WebDriverException as e:
            self.logger.error("WebDriverException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def get_cookie(self, cookie_name):
        try:
            self.logger.debug("Get page cookie: {0}".format(cookie_name))
            self.driver.get_cookie(cookie_name)
        except NoSuchCookieException as e:
            self.logger.error("NoSuchCookieException: {0}".format(e)[:-1])
            raise e
        except WebDriverException as e:
            self.logger.error("WebDriverException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def delete_cookies(self):
        try:
            self.logger.debug("Get page cookies")
            self.driver.delete_all_cookies()
        except WebDriverException as e:
            self.logger.error("WebDriverException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def delete_cookie(self, name, option_string):
        try:
            self.logger.debug("Get page cookies")
            self.driver.delete_cookie(name, option_string)
        except NoSuchCookieException as e:
            self.logger.error("NoSuchCookieException: {0}".format(e)[:-1])
            raise e
        except WebDriverException as e:
            self.logger.error("WebDriverException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def add_cookie(self, cookie_dict):
        try:
            self.logger.debug("Get page cookies")
            self.driver.add_cookie(cookie_dict)
        except UnableToSetCookieException as e:
            self.logger.error("NoSuchCookieException: {0}".format(e)[:-1])
            raise e
        except WebDriverException as e:
            self.logger.error("WebDriverException: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise

    def take_screenshot(self, filename):
        try:
            self.logger.debug("Get screenshot as {0}".format(filename))
            now_time = time.strftime("%Y-%m-%d_%H-%M-%S")
            _filename = '{0}_{1}.png'.format(now_time, filename)
            if not os.path.exists(RESULTS_SCREENSHOTS_PATH):
                os.makedirs(RESULTS_SCREENSHOTS_PATH)
            filepath = os.path.join(RESULTS_SCREENSHOTS_PATH, _filename)
            self.driver.get_screenshot_as_file(filepath)
        except WebDriverException as e:
            self.logger.error("WebDriverException: {0}".format(e)[:-1])
            raise e
        except IOError as e:
            self.logger.error("IOError: {0}".format(e)[:-1])
            raise e
        except Exception:
            self.logger.exception("Exception: ", exc_info=True)
            raise
